﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading.Tasks;

namespace HimVoiceStudioCore
{
    public class TranscriptEngine
    {
        public ComposingParam CurrentParam = new ComposingParam();
        public Dictionary<string, ComposingParam> defines = new Dictionary<string, ComposingParam>();
        public Dictionary<string, string> emodefines = new Dictionary<string, string>();
        private int lineNumber = 0;

        private int? pushedSeed = null;
        private string pushedEmotion = null;
        public ComposingParam pushedParam =null;

        public SpliceParam spliceParam =null;
        

        private void Reset()
        {
            defines.Clear();
            CurrentParam = new ComposingParam();
            lineNumber = 0;
        }


        public TranscriptEngine()
        {

        }

        private void EvaluateOneLine(string line)
        {
            lineNumber++;
            line = line.Trim();

            if (line.StartsWith("#")) { return; }
            if (line.Contains("#"))
            {
                line = line.Split('#')[0];
            }
            if (line.StartsWith("["))
            {
                try
                {
                   
                    if (line.StartsWith("[sfx ") && line.EndsWith("]"))
                    {
                        EvaluateSfx(ExtractParam(line, "[sfx ", "]"));
                        return;
                    }
                    if (line.StartsWith("[=") && line.EndsWith("]"))
                    {
                        EvaluateDelay(ExtractParam(line, "[=", "]"));
                        return;
                    }
                    if (line.StartsWith("[emo ") && line.EndsWith("]"))
                    {
                        EvaluatePushEmotion(ExtractParam(line, "[emo ", "]"));
                        return;
                    }
                    if (line.StartsWith("[setemo ") && line.EndsWith("]"))
                    {
                        EvaluateSetEmotion(ExtractParam(line, "[setemo ", "]"));
                        return;
                    }
                    if (line.StartsWith("[defemo ") && line.EndsWith("]"))
                    {
                        EvaluateDefineEmotion(ExtractParam(line, "[defemo ", "]"));
                        return;
                    }
                    if (line.StartsWith("[seed ") && line.EndsWith("]"))
                    {
                        EvaluatePushSeed(ExtractParam(line, "[seed ", "]"));
                        return;
                    }
                    if (line.StartsWith("[setseed ") && line.EndsWith("]"))
                    {
                        EvaluateSetSeed(ExtractParam(line, "[setseed ", "]"));
                        return;
                    }
                    if (line.StartsWith("[splice ") && line.EndsWith("]"))
                    {
                        EvaluateSplice(ExtractParam(line, "[splice ", "]"));
                        return;
                    }
                }
                catch(TranscriptSyntaxException e)
                {
                    throw new TranscriptSyntaxException(e.lineNumber, line, e.reason);
                }
                throw new TranscriptSyntaxException(lineNumber, line,"未知或无效的指令行");
            }
            else
            {
                if (!string.IsNullOrWhiteSpace(line))
                {

                    doCompose(line);
                }
            }
        }

        public ComposingParam PeekCurrentState()
        {
            ComposingParam param = null;
            if ( pushedParam != null)
            {
                param = pushedParam.Clone();
            }
            if (param == null)
            {
                param = CurrentParam.Clone();
            }
            
            if (pushedEmotion != null)
            {
                param.emotion = pushedEmotion;
                pushedEmotion = null;
            }
            if (pushedSeed.HasValue)
            {
                param.seed = pushedSeed.Value;
                pushedSeed = null;
            }
            return param;
        }

        private void doCompose(string str,ComposingParam param = null)
        {
            if(param != null && pushedParam != null)
            {
                pushedParam = null;
            }
            if(pushedParam != null)
            {
                param = pushedParam;
                param.seed = CurrentParam.seed;
                param.emotion = CurrentParam.emotion;
                pushedParam = null;
            }
            if(param == null) { 
                param = CurrentParam;
            }
            param = param.Clone();

            if(pushedEmotion != null)
            {
                param.emotion = pushedEmotion;
                pushedEmotion = null;
            }
            if (pushedSeed.HasValue)
            {
                param.seed = pushedSeed.Value;
                pushedSeed = null;
            }
            if(spliceParam != null)
            {
                param.spliceParam = spliceParam;
                spliceParam = null;
            }
            throw new YieldComposeException(param, str);
        }

        private string ExtractParam(string line, string prefix, string suffix)
        {
            line = line.Substring(prefix.Length);
            line = line.Substring(0, line.Length - suffix.Length);
            return line;
        }

        private string ExtractParamAndTailingContent(string line, string prefix, string suffix, out string content)
        {
            line = line.Substring(prefix.Length);
            if (line.Contains(suffix))
            {
                string param = line.Substring(0,line.IndexOf(suffix));
                content = line.Substring(param.Length + suffix.Length);
                return param;
            }
            throw new TranscriptSyntaxException(lineNumber, line, "无效的指令行：缺少 ]");
        }

        private Dictionary<string,string> strToDict(string str)
        {
            string[] paramPairs = str.Split(new char[] { ',' },StringSplitOptions.RemoveEmptyEntries);
            var dict = new Dictionary<string,string>();
            foreach (var item in paramPairs)
            {
                if (item.Contains("="))
                {
                    string[] data = item.Split(new char[] { '=' });
                    if (dict.ContainsKey(data[0]))
                    {
                        throw new TranscriptSyntaxException(lineNumber, null, "重复的参数：" + data[0]);
                    }
                    dict.Add(data[0], data[1]);
                }
                else
                {
                    throw new TranscriptSyntaxException(lineNumber, null, "无效参数列表");
                }
            }
            return dict;
        }

        
       
        private void EvaluateSfx(string line)
        {
            var splice = this.spliceParam;
            if(this.spliceParam != null)
            {
                this.spliceParam = null;
            }
            throw new YieldInsertFile(line) { spliceParam = splice };
        }

        private void EvaluateSplice(string line)
        {
            string[] nums = line.Split(new char[] { '-' },StringSplitOptions.RemoveEmptyEntries);
            if(nums.Length != 2)
            {
                throw new TranscriptSyntaxException(lineNumber, line, "无效的裁剪区间格式，请查询语法参考");
            }
            if (!float.TryParse(nums[0], out float start)) throw new TranscriptSyntaxException(lineNumber, nums[0], "无效的裁剪区间数字格式");
            if (!float.TryParse(nums[1], out float end)) throw new TranscriptSyntaxException(lineNumber, nums[1], "无效的裁剪区间数字格式");
            if(start < 0 || end <= start) throw new TranscriptSyntaxException(lineNumber, nums[1], "无效的裁剪区间数字范围");
            this.spliceParam = new SpliceParam() {start = start,end = end };
        }
        private void EvaluateSetSeed(string line)
        {
            if(int.TryParse(line, out int seed))
            {
                CurrentParam.seed = seed;
            }
            else
            {
                throw new TranscriptSyntaxException(lineNumber, line, "种子格式无效");
            }
        }

        private void EvaluatePushSeed(string line)
        {
            if (int.TryParse(line, out int seed))
            {
                pushedSeed = seed;
            }
            else
            {
                throw new TranscriptSyntaxException(lineNumber, line, "种子格式无效");
            }
        }

        private void EvaluateSetEmotion(string line)
        {
            if (line.StartsWith("@"))
            {
                string key = line.Substring(1);
                if (emodefines.ContainsKey(key))
                {
                    line = emodefines[key]; 
                }
                else
                {
                    throw new TranscriptSyntaxException(lineNumber, line, "找不到情感参考定义：" + key);
                }
            }
            CurrentParam.emotion = line;
        }

        private void EvaluatePushEmotion(string line)
        {
            if (line.StartsWith("@"))
            {
                string key = line.Substring(1);
                if (emodefines.ContainsKey(key))
                {
                    line = emodefines[key];
                }
                else
                {
                    throw new TranscriptSyntaxException(lineNumber, line, "找不到情感参考定义：" + key);
                }
            }
            pushedEmotion = line;
        }

        private void EvaluateDefineEmotion(string line)
        {
            string[] defines = line.Split(' ');
            if(defines.Length < 2)
            {
                throw new TranscriptSyntaxException(lineNumber, line,"emodef 需要两个参数：name,emo，但只有" + defines.Length+"个参数");
            }
            string defkey = defines[0];
            string defvalue = defines[1];
            if (emodefines.ContainsKey(defkey))
            {
                emodefines[defkey]= defvalue; 
            }
            else
            {
                emodefines.Add(defkey, defvalue);
            }
        }


       

        private void EvaluateDelay(string line)
        {
            float multiplayer = 1000;
            if (line.EndsWith("ms"))
            {
                multiplayer = 1;
            }
            line = line.Replace("m", "").Replace("s","");
            if(!float.TryParse(line, out float val)) { throw new TranscriptSyntaxException(lineNumber, null, "无效的数字格式：" + line); }
            int delayMs = (int)(val * multiplayer);
            throw new YieldDelayException(delayMs);
        }


        private void EvaluateNoExecute(string script)
        {
            List<string> lines = splitLines(script);
            foreach (var line in lines)
            {
                try
                {
                    EvaluateOneLine(line);
                }
                catch (BaseParamYieldException)
                {
                    // Do nothing. 
                }
            }
        }

        private List<string> splitLines(string str)
        {
            return str.Replace("\r", "").Split(new char[] { '\n' }, StringSplitOptions.None).ToList();
        }

        private List<BaseParamYieldException> EvaluateExecute(string script)
        {
            List<BaseParamYieldException> result = new List<BaseParamYieldException>();
            List<string> lines = splitLines(script);
            foreach (var line in lines)
            {
                try
                {
                    EvaluateOneLine(line);
                }
                catch (BaseParamYieldException ex)
                {
                    result.Add(ex);
                }
            }
            return result;
        }

        public void Validate(string str)
        {
            Reset();
            EvaluateNoExecute(str);
        }
        public List<BaseParamYieldException> EvaluateAll(string lines)
        {
            Reset();
            return EvaluateExecute(lines);
        }

        public List<BaseParamYieldException> EvaluatePartial(string precondition, string selection)
        {
            Reset();
            EvaluateNoExecute(precondition);
            return EvaluateExecute(selection);
        }
    }

    public class SpliceParam
    {
        public float start, end;
    }

    public class TranscriptSyntaxException : Exception
    {
        public int lineNumber;
        public string lineContent;
        public string reason;

        public TranscriptSyntaxException(int lineNumber, string lineContent, string reason)
        {
            this.lineNumber = lineNumber;
            this.lineContent = lineContent;
            this.reason = reason;
        }
    }

    public class BaseParamYieldException : Exception { }
    public class YieldDelayException : BaseParamYieldException
    {
        public int delayMs = 0;
        public YieldDelayException(int ms)
        {
            delayMs = ms;
        }
    }
    public class YieldComposeException : BaseParamYieldException
    {
        public ComposingParam param;
        public string text;

        public YieldComposeException(ComposingParam param, string text)
        {
            this.param = param.Clone();
            this.text = text;
        }
    }

    public class YieldInsertFile : BaseParamYieldException
    {
        public string fileName;
        public SpliceParam spliceParam;
        public YieldInsertFile(string fileName)
        {
            this.fileName = fileName;
        }
    }
}
